package com.tzu.ft.base;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

import com.tzu.ft.transfer.ClientUI;
import com.tzu.ft.transfer.ServerUI;
import com.tzu.utils.JSONTools;
import com.tzu.utils.StringTools;

/**
 * 传输工具
 */
public class TransferTools {

    /**
     * 默认socket服务端口
     */
    public static final int DEFAULT_SOCKET_PORT = 12001;
    public static final String DEFAULT_SOCKET_IP = "127.0.0.1";

    // 默认不超时
    public static final int DEFAULT_SO_TIMEOUT = 0;

    private static final int EOF = -1;
    private static final int KSIZE = 1024;

    private static final long FLUSH_SIZE = 1024 * 1024 * 10;

    /**
     * 测速间隔
     */
    private static final int SPEED_INTERVAL = 1000;

    /**
     * 获取数据流对象
     */
    public static DataInputStream getDataInputStream(InputStream inputStream) {
        return new DataInputStream(inputStream);
    }

    /**
     * 获取数据流对象
     */
    public static DataOutputStream getDataOutputStream(OutputStream outputStream) {
        return new DataOutputStream(outputStream);
    }

    /**
     * 读取信息
     */
    public static String readInfoToString(DataInputStream dataInputStream) throws IOException {
        String msg = dataInputStream.readUTF();
        System.out.println(msg);
        return msg;
    }

    /**
     * 读取信息
     */
    public static <T> T readInfo(DataInputStream dataInputStream, Class<T> clazz) throws IOException {
        String msg = readInfoToString(dataInputStream);
        return JSONTools.toObject(msg, clazz);
    }

    /**
     * 接收写入文件
     */
    public static void receiveFile(DataInputStream dataInputStream, File tofile, long fileLen, long startLen,
                                   ServerUI ui) throws IOException {
        // 经测试，FileInputStream比RandomAccessFile效率高
//      RandomAccessFile randomAccessFile = new RandomAccessFile(tofile, "rwd");

        long begintime = System.currentTimeMillis();
        System.out.println("接收文件大小：" + takeFileSize(fileLen, 3));

        long count = 0;
        int n;

        FileOutputStream fileOutputStream = null;

        if (startLen > 0) {
//            randomAccessFile.seek(startLen);
            fileOutputStream = new FileOutputStream(tofile, true);
            count = startLen;
        } else {
            fileOutputStream = new FileOutputStream(tofile);
        }

        // 计时器
        long timea = System.currentTimeMillis();
        long sizea = count;

        String fileext = getFileExt(tofile.getName());
        String filesize = takeFileSize(fileLen, 3);
        String usedtime = takeUsedTime(begintime, System.currentTimeMillis());
        double precent = takeTransferPrecent(count, fileLen);
        String speed = takeSpeed(System.currentTimeMillis() - timea, count - sizea);
        // 显示信息
        ui.processStatus(fileext, filesize, usedtime, precent, speed);

        // 获取缓冲大小
        final byte[] buffer = new byte[FTConfig.getStreamBufferSize() * 1024];

        while (EOF != (n = dataInputStream.read(buffer))) {
            fileOutputStream.write(buffer, 0, n);
            count += n;

//			if (count % FLUSH_SIZE == 0) {
////				System.out.println("传输进度：" + (100 * count / fileLen) + "%");
//			}
            // 测速
            if (System.currentTimeMillis() - timea > SPEED_INTERVAL) {
                // 显示信息
                ui.processStatus(fileext, filesize, takeUsedTime(begintime, System.currentTimeMillis()),
                        takeTransferPrecent(count, fileLen),
                        takeSpeed(System.currentTimeMillis() - timea, count - sizea));
                timea = System.currentTimeMillis();
                sizea = count;
            }

        }
        // 本地文件刷新并关闭
        fileOutputStream.close();
//		System.out.println("传输进度：" + showTransferPrecent(fileLen, fileLen));
    }

    /**
     * 发送消息
     */
    public static void sendInfo(DataOutputStream dataOutputStream, String msg) throws IOException {
        dataOutputStream.writeUTF(msg);
        dataOutputStream.flush();
    }

    /**
     * 发送文件
     */
    public static void sendFile(DataOutputStream dataOutputStream, File file, long startLen, ClientUI ui)
            throws IOException {
        long filelen = file.length();

        long begintime = System.currentTimeMillis();

        System.out.println("发送文件大小：" + takeFileSize(filelen, 3));

        // 只读模式打开文件
//      RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
        FileInputStream fileInputStream = new FileInputStream(file);

        long count = 0;
        int n;

        if (startLen > 0) {
            // 移动位置
//          randomAccessFile.seek(startLen);
            fileInputStream.skip(startLen);
            count = startLen;
        }

        // 计时器
        long timea = System.currentTimeMillis();
        long sizea = count;

        String fileext = getFileExt(file.getName());
        String filesize = takeFileSize(filelen, 3);
        String usedtime = takeUsedTime(begintime, System.currentTimeMillis());
        double precent = takeTransferPrecent(count, filelen);
        String speed = takeSpeed(System.currentTimeMillis() - timea, count - sizea);

        // 显示信息
        ui.processStatus(fileext, filesize, usedtime, precent, speed);

        // 获取缓冲大小，默认单位KiB
        final byte[] buffer = new byte[FTConfig.getStreamBufferSize() * 1024];

        while (EOF != (n = fileInputStream.read(buffer))) {
            dataOutputStream.write(buffer, 0, n);
            count += n;

//			// 数据刷新间隔
//			if (count % FLUSH_SIZE == 0) {
////                dataOutputStream.flush();
//			}
            // 测速
            if (System.currentTimeMillis() - timea > SPEED_INTERVAL) {
                // 显示信息
                ui.processStatus(fileext, filesize, takeUsedTime(begintime, System.currentTimeMillis()),
                        takeTransferPrecent(count, filelen),
                        takeSpeed(System.currentTimeMillis() - timea, count - sizea));
                timea = System.currentTimeMillis();
                sizea = count;
            }
        }
        // 刷新缓冲
        dataOutputStream.flush();
        fileInputStream.close();
//		System.out.println("传输进度：" + showTransferPrecent(filelen, filelen));

    }

    /**
     * 获取文件夹大小
     */
    public static long takeFileOrDirSize(String path) {
        File file = new File(path);
        if (file.exists()) {
            if (file.isFile()) {
                return file.length();
            } else if (file.isDirectory()) {
                System.out.println(takeFileSize(file.length(), 3));
                System.out.println(takeFileSize(file.getTotalSpace(), 3));
                System.out.println(takeFileSize(file.getUsableSpace(), 3));
                System.out.println(takeFileSize(file.getFreeSpace(), 3));
                return file.getUsableSpace();
            }
        }
        return 0;
    }

    /**
     * 显示文件大小
     *
     * @param filesize 文件大小（字节）
     * @param pointlen 文件大小保留小数点位数
     */
    public static String takeFileSize(long filesize, int pointlen) {
        double show = filesize;
        if (show < KSIZE) {
            return show + " Byte";
        }
        show /= KSIZE;
        if (show < KSIZE) {
            show = StringTools.roundMid(show, pointlen);
            return show + " KiB";
        }
        show /= KSIZE;
        if (show < KSIZE) {
            show = StringTools.roundMid(show, pointlen);
            return show + " MiB";
        }
        show /= KSIZE;
        if (show < KSIZE) {
            show = StringTools.roundMid(show, pointlen);
            return show + " GiB";
        }
        show /= KSIZE;
        if (show < KSIZE) {
            show = StringTools.roundMid(show, pointlen);
            return show + " TiB";
        }
        show /= KSIZE;
        if (show < KSIZE) {
            show = StringTools.roundMid(show, pointlen);
            return show + " PiB";
        }
        // 最终单位，long的最大值为8EiB
        show /= KSIZE;
        show = StringTools.roundMid(show, pointlen);
        return String.valueOf(show).concat(" EiB");
    }

    /**
     * 显示速度
     */
    public static String takeSpeed(long time, long size) {
        long second = time / 1000;
        long speed = 0;
        if (second > 0) {
            speed = size / second;
        }
        return takeFileSize(speed, 2) + "/s";

    }

    /**
     * 显示已用时长
     */
    public static String takeUsedTime(long start, long now) {
        long time = now - start;
        long hours = time / (1000 * 60 * 60);
        long minutes = (time - hours * (1000 * 60 * 60)) / (1000 * 60);
        long second = (time - hours * (1000 * 60 * 60) - minutes * (1000 * 60)) / 1000;
        String show = second + "s";
        show = minutes + "m " + show;
        show = hours + "h " + show;
        return show;
    }

    /**
     * 显示传输进度
     */
    public static double takeTransferPrecent(long current, long total) {
        //修正分母为0的情况
        if (total == 0L) {
            return 0;
        }
        double precnet = (double) current / (double) total;
        precnet *= 100;
        precnet = StringTools.roundMid(precnet, 2);
        return precnet;
    }

    /**
     * 获取文件扩展名
     *
     * @return
     */
    public static String getFileExt(String filename) {
        int index = filename.lastIndexOf(".");
        if (index == -1) {
            return null;
        }
        String result = filename.substring(index + 1);
        return result;
    }

    /**
     * 客户端优化
     */
    public static Socket settingClient(Socket socket) throws SocketException {
//		socket.setSendBufferSize(16 * 1024 * 1024);
//		socket.setReceiveBufferSize(4 * 1024);
        socket.setPerformancePreferences(0, 1, 2);
//		socket.setTcpNoDelay(true);
        socket.setSoTimeout(0);
        socket.setKeepAlive(true);
        return socket;
    }

    /**
     * 服务端优化
     */
    public static Socket settingServer(Socket socket) throws SocketException {
//		socket.setSendBufferSize(4 * 1024);
//		socket.setReceiveBufferSize(16 * 1024 * 1024);
//		socket.setPerformancePreferences(0, 1, 2);
//		socket.setTcpNoDelay(true);
        socket.setSoTimeout(0);
        socket.setKeepAlive(true);
        return socket;
    }

    /**
     * 服务端优化
     */
    public static ServerSocket settingServerSocket(ServerSocket serverSocket) throws SocketException {
//		serverSocket.setReceiveBufferSize(32 * 1024 * 1024);
        // 设置性能参数，可设置任意整数，数值越大，相应的参数重要性越高（连接时间，延迟，带宽）
//		serverSocket.setPerformancePreferences(0, 1, 2);
        serverSocket.setSoTimeout(0);
        return serverSocket;
    }

    /**
     * 获取本机IP
     */
    public static String getLocalIp() {
        try {
            InetAddress addr = InetAddress.getLocalHost();
            return addr.getHostAddress();
//            System.out.println(addr.getHostAddress());
//            String hostname = addr.getHostName();
//            System.out.println("Local host name: " + hostname);
        } catch (UnknownHostException e) {
            e.printStackTrace();
            return DEFAULT_SOCKET_IP;
        }

    }

    /**
     * 获取该文件夹下所有文件，包括子文件夹内的
     */
    public static List<File> takeAllFiles(File dir) {
        List<File> list = new ArrayList<>();
        File[] files = dir.listFiles();
        if (null != files) {
            for (int i = 0; i < files.length; i++) {
                File file = files[i];
                if (file.isFile()) {
                    list.add(file);
                } else {
                    list.addAll(takeAllFiles(file));
                }
            }
        }
        return list;
    }

    /**
     * 获取该文件夹下所有文件，包括子文件夹内的
     */
    public static long takeAllFileSize(File dir) {
        List<File> list = takeAllFiles(dir);
        long filesize = 0;
        for (File file : list) {
            filesize += file.length();
        }
        return filesize;
    }

    /**
     * 获取从基础路径开始到文件之间的路径
     */
    public static String getMiddleParentPath(File basefile, File file) {
        if (null == basefile || null == file) {
            return null;
        }
        String rootParentPath = basefile.getParent();
        String fileParentPath = file.getParent();
        String path = fileParentPath.replace(rootParentPath, "");
        return path;
    }


    private static void testThreadPoolExecutor() throws InterruptedException {
        ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
        for (int i = 0; i < 18; i++) {
            System.out.println(i);
            executor.execute(() -> {
                int jm = (int) (Math.random() * 10);
                jm += 10;
                for (int j = 0; j < jm; j++) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }

        while (true) {
            System.out.println("===========================");
            System.out.println("活动" + executor.getActiveCount());
            System.out.println("任务" + executor.getTaskCount());
            System.out.println("完成任务" + executor.getCompletedTaskCount());
//            System.out.println("核心池" + executor.getCorePoolSize());
//            System.out.println("large池" + executor.getLargestPoolSize());
//            System.out.println("max池" + executor.getMaximumPoolSize());
//            System.out.println("池" + executor.getPoolSize());
            System.out.println("队列" + executor.getQueue().size());
            executor.execute(() -> {
                System.currentTimeMillis();
            });
        }
    }

    // ========================================================================
    public static void main(String[] args) {
        try {
            testThreadPoolExecutor();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

}
